package com.example.zpositionanimationdemo.materialripple;

import com.example.zpositionanimationdemo.chechbox.TypedAttrUtils;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

public class MaterialRippleLayout extends StackLayout
        implements Component.TouchEventListener,Component.DrawTask,Component.ClickedListener {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MaterialRippleLayout");
    private static final int DEFAULT_LONG_PRESS_TIMEOUT = 500;
    private static final boolean IS_DEFAULT_DELAY_CLICK = true;
    private static final float DEFAULT_ALPHA = 0.2f;
    private static final int DEFAULT_DURATION = 350;
    private static final float DEFAULT_DIAMETER_DP = 80;
    private static final int HOVER_DURATION = 1500;
    private static final int ADD_RADIUS_LENGTH = 20;
    private int rippleDuration = DEFAULT_DURATION;
    private static final boolean IS_DEFAULT_RIPPLE_OVERLAY = false;
    private static final Color DEFAULT_BACKGROUND = Color.TRANSPARENT;
    private static final int DEFAULT_RIPPLE_COLOR = 0x80000000;
    private static final int INTAGER2 = 2;
    private static final int INTAGER_SIX = 6;
    private static final int INTAGER3 = 3;
    private static final int TIME = 200;
    MmiPoint pointerPosition;
    DelayRunAnimator delayRunAnimator = new DelayRunAnimator();

    private final AnimatorValue.ValueUpdateListener mAnimatorUpdateListener = new AnimatorValue.ValueUpdateListener() {
        @Override
        public void onUpdate(AnimatorValue animatorV, float value) {
            if (value <= 0) {
                return;
            }
            double currentRadius = rippleDiameter + maxRadius * value;
            if (currentRadius < rippleDiameter) {
                setRadius(rippleDiameter);
            } else {
                setRadius(currentRadius);
            }
            invalidate();
        }
    };

    private Component childView;
    private Color rippleColor = Color.BLACK;
    private float rippleAlpha = DEFAULT_ALPHA;
    private float rippleDiameter = DEFAULT_DIAMETER_DP;
    private boolean isRippleOverlay;
    private boolean isRippleInAdapter;
    private ShapeElement mBackgroundUnable;
    private ShapeElement mBackgroundPressed;
    private final Paint mPaint = new Paint();
    private EventHandler handler;

    private AnimatorValue animatorValue;

    private ListContainer parentAdapter;
    private boolean isLongClick = false;
    private double maxRadius;
    private double mRadiusRipple;
    private boolean isFingerUp = true;
    private double positionX;
    private double positionY;
    private OnClickButton mclickButton;

    /**
     * MaterialRippleLayout
     *
     * @param context 上下文
     */
    public MaterialRippleLayout(Context context) {
        super(context);
        init(context, null, "");
    }

    /**
     * MaterialRippleLayout
     *
     * @param context 上下文
     * @param attrSet attrSet
     */
    public MaterialRippleLayout(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init(context, attrSet, "");
    }

    /**
     * MaterialRippleLayout
     *
     * @param context 上下文
     * @param attrSet attrSet
     * @param styleName styleName
     */
    public MaterialRippleLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(context, attrSet, styleName);
    }

    public double getX() {
        return positionX;
    }

    public void setX(double ppx) {
        this.positionX = ppx;
    }

    public double getY() {
        return positionY;
    }

    public void setY(double ppy) {
        this.positionY = ppy;
    }

    public double getRadius() {
        return mRadiusRipple;
    }

    public void setRadius(double radius) {
        this.mRadiusRipple = radius;
    }

    private void init(Context context, AttrSet attrs, String defStyleAttr) {
        handler = new EventHandler(EventRunner.getMainEventRunner());
        initAttrs(context, attrs, defStyleAttr);
        setupPaint();

        addDrawTask(this::onDraw);
        setTouchEventListener(this::onTouchEvent);
    }

    private void initAttrs(Context context, AttrSet attrSet, String defStyleAttr) {
        setStatePress();
        if (attrSet == null) {
            return;
        }
        rippleColor = TypedAttrUtils.getColor(attrSet, "mrl_rippleColor", new Color(DEFAULT_RIPPLE_COLOR));
    }

    /**
     * 初始化画笔
     */
    private void setupPaint() {
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setColor(rippleColor);
        mPaint.setAlpha(rippleAlpha);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        HiLog.info(LABEL, "initAttrs onDraw getWidth = " + getWidth() + "   getRadius() : " + getRadius());
        canvas.drawCircle((float) getX(),(float)getY(),(float)getRadius(), mPaint);
    }

    private void setStatePress() {
        mBackgroundUnable = new ShapeElement();
        mBackgroundUnable.setRgbColor(RgbColor.fromArgbInt(Color.TRANSPARENT.getValue()));
        mBackgroundPressed = new ShapeElement();
        mBackgroundPressed.setRgbColor(RgbColor.fromArgbInt(Color.BLUE.getValue()));
        int[][] states = new int[INTAGER_SIX][];
        states[0] = new int[]{ComponentState.COMPONENT_STATE_DISABLED};
        states[1] = new int[]{ComponentState.COMPONENT_STATE_PRESSED};
        states[INTAGER2] = new int[]{ComponentState.COMPONENT_STATE_FOCUSED};
        states[INTAGER3] = new int[]{ComponentState.COMPONENT_STATE_EMPTY};
        StateElement stateBackground = new StateElement();
        stateBackground.addState(states[0], mBackgroundUnable);
        stateBackground.addState(states[1], mBackgroundPressed);
        stateBackground.addState(states[INTAGER2], mBackgroundPressed);
        stateBackground.addState(states[INTAGER3], mBackgroundUnable);
    }

    /**
     * 点击事件的动画
     */
    private void initAnimator() {
        animatorValue = new AnimatorValue();
        animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        animatorValue.setDuration(rippleDuration);
        animatorValue.setValueUpdateListener(mAnimatorUpdateListener);
        animatorValue.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                setRadius(0);
                fingerUp();

                if (mclickButton != null) {
                    mclickButton.onClick();
                }
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
        animatorValue.start();
    }

    public interface OnClickButton {
        void onClick();
    }

    /**
     * 长按事件动画
     */
    private void initLongClickAnimator() {
        animatorValue = new AnimatorValue();
        animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        animatorValue.setDuration(rippleDuration);
        animatorValue.setValueUpdateListener(mAnimatorUpdateListener);
        animatorValue.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                if (isRippleOverlay && isFingerUp) {
                    setRadius(0);
                    invalidate();
                }
                if (!isRippleOverlay) {
                    setRadius(0);
                    invalidate();
                }
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
        animatorValue.start();
    }

    @SuppressWarnings("unchecked")
    public <T extends Component> T getChildView() {
        return (T) childView;
    }

    @Override
    public void addComponent(Component childComponent) {
        if (getChildCount() > 0) {
            throw new IllegalStateException("");
        }
        childView = childComponent;
        super.addComponent(childComponent);
    }

    @Override
    public void setClickedListener(ClickedListener listener) {
        if (childView == null) {
            throw new IllegalStateException("");
        }
        childView.setClickedListener(listener);
    }

    @Override
    public void setLongClickedListener(LongClickedListener listener) {
        if (childView == null) {
            throw new IllegalStateException("");
        }
        childView.setLongClickedListener(listener);
    }

    @Override
    public void onClick(Component component) {
        isLongClick = false;
        rippleDuration = DEFAULT_DURATION;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        touchEvent.getRadius(0);
        pointerPosition = touchEvent.getPointerPosition(0);
        double ppx = pointerPosition.getX();
        int[] parentLocationOnScreen = getLocationOnScreen();
        setX(ppx - parentLocationOnScreen[0]);
        setY((double) component.getHeight() / INTAGER2);
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                isFingerUp = false;
                rippleDuration = DEFAULT_DURATION;
                handler.postTask(delayRunAnimator, TIME);
                setRippleDuration(rippleDuration);

                if (Math.abs(ppx - parentLocationOnScreen[0])
                        > Math.abs(ppx - (parentLocationOnScreen[0] + getWidth()))) {
                    maxRadius = Math.abs(ppx - parentLocationOnScreen[0]) + ADD_RADIUS_LENGTH;
                } else {
                    maxRadius = Math.abs(ppx - (parentLocationOnScreen[0] + getWidth())) + ADD_RADIUS_LENGTH;
                }
                mRadiusRipple = maxRadius;
                fingerPress();
                resetConfig();
                return true;
            case TouchEvent.POINT_MOVE:
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                isFingerUp = true;
                if (!isLongClick) {
                    handler.removeTask(delayRunAnimator);
                    if (rippleDuration <= 0) {
                        rippleDuration = DEFAULT_DURATION;
                    }
                    setRippleDuration(rippleDuration);
                    initAnimator();
                }
                if (isLongClick) {
                    fingerUp();
                }
                break;
            default:
                break;
        }
        return false;
    }

    private class DelayRunAnimator implements Runnable {
        @Override
        public void run() {
            isLongClick = true;
            rippleDuration = HOVER_DURATION;
            initLongClickAnimator();
        }
    }

    private void performalViewClick() {
        if (getComponentParent() instanceof ListContainer) {
            if (!childView.callOnClick()) {
                clickAdapterView((ListContainer) getComponentParent());
            }
        } else if (isRippleInAdapter) {
            clickAdapterView(findParentAdapterView());
        } else {
            if (childView != null) {
                childView.callOnClick();
            }
        }
    }

    private void clickAdapterView(ListContainer parent) {
        final int position = parent.getIndexForComponent(MaterialRippleLayout.this);
        final long itemId = parent.getItemProvider() != null
                ? parent.getItemProvider().getItemId(position)
                : 0;
        if (position != ListContainer.INVALID_INDEX) {
            parent.executeItemClick(MaterialRippleLayout.this, position, itemId);
        }
    }

    private class PerformClickEvent implements Runnable {
        @Override
        public void run() {
            if (getComponentParent() instanceof ListContainer) {
                if (!childView.callOnClick()) {
                    clickAdapterView((ListContainer) getComponentParent());
                }
            } else if (isRippleInAdapter) {
                clickAdapterView(findParentAdapterView());
            } else {
                if (childView != null) {
                    childView.callOnClick();
                }
                callOnClick();
            }
        }

        private void clickAdapterView(ListContainer parent) {
            final int position = parent.getIndexForComponent(MaterialRippleLayout.this);
            final long itemId = parent.getItemProvider() != null
                    ? parent.getItemProvider().getItemId(position)
                    : 0;
            if (position != ListContainer.INVALID_INDEX) {
                parent.executeItemClick(MaterialRippleLayout.this, position, itemId);
            }
        }
    }

    private ListContainer findParentAdapterView() {
        if (parentAdapter != null) {
            return parentAdapter;
        }
        ComponentParent current = getComponentParent();
        while (true) {
            if (current instanceof ListContainer) {
                parentAdapter = (ListContainer) current;
                return parentAdapter;
            } else {
                if (current != null) {
                    current = current.getComponentParent();
                }
            }
        }
    }

    private void resetConfig() {
        setRadius(getRadius());
    }

    /**
     * 按下
     */
    private void fingerPress() {
    }

    /**
     * 抬起
     */
    private void fingerUp() {
        if (isRippleOverlay && isFingerUp) {
            setRadius(0);
            invalidate();
        }
        isLongClick = false;
    }

    public void setRippleDuration(int aRippleDuration) {
        this.rippleDuration = aRippleDuration;
    }
}
